#pragma semicolon 1    ///WE RECOMMEND THE SEMICOLON

#include <sourcemod>
#include "W3SIncs/War3Source_Interface"

public Plugin:myinfo = 
{
    name = "War3Source Race - Sacred Warrior",
    author = "Ted Theodore Logan",
    description = "The Sacred Warrior race for War3Source.",
    version = "1.1",
};

new thisRaceID;
new SKILL_VITALITY, SKILL_SPEAR, SKILL_BLOOD, ULT_BREAK;

// Inner Vitality, HP healed
new VitalityHealed[]={0,1,2,3,4}; // How much HP Vitality heals each second

// Burning Spear stacking effect
new SpearDamage[]={0,1,2,3,4}; // How much damage does a stack do?
new SpearStacks[]={1,1,2,2,3}; // How many stacks can the attacker dish out?
new Float:SpearUntil[MAXPLAYERS]; // Until when is the victim affected?
new ClientSpearStacks[MAXPLAYERS]; // How many stacks does the victim have?
new bool:bSpeared[MAXPLAYERS]; // Is this player speared (has DoT on him)?
new SpearedBy[MAXPLAYERS]; // Who was the victim speared by?
new bool:bSpearActivated[MAXPLAYERS]; // Does the player have Burning Spear activated?

// Buffs that berserker applys
new Float:BerserkerBuffDamage[]={0.0,0.005,0.01,0.015,0.02};  // each 7% you add one of these 
new Float:BerserkerBuffASPD[]={0.0,0.01,0.02,0.03,0.04};      // to get the total buff...

// Life Break costs / damage dealt
new Float:LifeBreakHPConsumed[]={0.0,0.75,0.65,0.55,0.45}; // Percentage of how much HP the caster loses
new Float:LifeBreakHPTaken[]={0.0,0.25,0.35,0.45,0.60};    // Percentage of how much HP the victim loses

public OnWar3PluginReady(){
    thisRaceID=War3_CreateNewRace("Sacred Warrior","sacred");
    SKILL_VITALITY=War3_AddRaceSkill(thisRaceID,"Inner Vitality","Passively recover HP. When below 40% you heal twice as fast.",false,4);
    SKILL_SPEAR=War3_AddRaceSkill(thisRaceID,"Burning Spear","Passively lose HP, but set enemys ablaze. The more you hit them, the more damage they take",false,4);
    SKILL_BLOOD=War3_AddRaceSkill(thisRaceID,"Berserkers Blood","Passively gain damage and attack speed as your health sinks.",false,4);
    ULT_BREAK=War3_AddRaceSkill(thisRaceID,"Life Break","Damage yourself to deal a great amount of damage",true,4); 
    War3_CreateRaceEnd(thisRaceID); ///DO NOT FORGET THE END!!!
}

public OnPluginStart()
{
    CreateTimer(1.0,InnerVitalityTimer,_,TIMER_REPEAT); // Healing Timer
    CreateTimer(0.5,BerserkerTimer,_,TIMER_REPEAT);      // Berserker ASPD Buff timer
    CreateTimer(1.0,BurningSpearTimer,_,TIMER_REPEAT);  // Burning Spear DoT Timer
}

public OnWar3EventSpawn(client)
{
    bSpeared[client] = false;            // Remove DoT and 
    ClientSpearStacks[client] = 0;  // deactivate Burning Spear
    bSpearActivated[client] = false;  // on spawn
}

public Action:BurningSpearTimer(Handle:h,any:data)
{
    new attacker;
    new damage;
    new SelfDamage;
    new skill;
    for(new i=1;i<=MaxClients;i++) // Iterate over all clients
    {
        if(ValidPlayer(i,true))
        {
            if(bSpearActivated[i]) // Client has Burning Spear activated
            {
            SelfDamage = RoundToCeil(War3_GetMaxHP(i) * 0.05);
            War3_DealDamage(i,SelfDamage,i,_,"burningspear"); // damage the client for having it activated
            }
        
            if(bSpeared[i])
            {
                attacker = SpearedBy[i];
                skill = War3_GetSkillLevel(attacker, thisRaceID, SKILL_SPEAR);
                if(bSpearActivated[attacker]) // Attacker has Burning Spear activated
                {
                    damage = ClientSpearStacks[i] * SpearDamage[skill]; // Number of stacks on the client * damage of the attacker
                    if(ValidPlayer(attacker, true))
                    {
                        if(War3_GetGame()==Game_TF)
                        {
                            War3_DealDamage(i,damage,attacker,_,"bleed_kill"); // Bleeding Icon
                        }
                        else
                        {
                            War3_DealDamage(i,damage,attacker,_,"burningspear"); // Generic skill name
                        }
                    }
                }
                
                if(GetGameTime()>SpearUntil[i]) // Time to do damage over?
                {
                    bSpeared[i] = false;         // Deactivate
                    ClientSpearStacks[i] = 0;  // Reset stacks
                }
            }
        }
    }                
}

public Action:InnerVitalityTimer(Handle:timer,any:userid)
{
    if(thisRaceID>0)
    {
        for(new i=1;i<=MaxClients;i++)
        {
            if(ValidPlayer(i,true))
            {
                if((War3_GetRace(i)==thisRaceID) && (!bSpearActivated[i])) // client playing Sacred Warrior and not in Burning Spear mode
                {
                    new skill = War3_GetSkillLevel(i,thisRaceID,SKILL_VITALITY);
                    new VictimCurHP = GetClientHealth(i);
                    new VictimMaxHP = War3_GetMaxHP(i);
                    new Float:DoubleTrigger = VictimMaxHP * 0.4;
                    new HPtoHeal = 0;
                    if(VictimCurHP<VictimMaxHP)
                    {
                        if(VictimCurHP<=DoubleTrigger) // Victim is below or at 40% health
                        {
                            HPtoHeal = VitalityHealed[skill] * 2;
                        }
                        else
                        {
                            HPtoHeal = VitalityHealed[skill];
                        }
                        War3_HealToMaxHP(i, HPtoHeal);
                    }
                }
            }
        }
    }
}

public CalcBuffAmount(client) // How many buffs to apply
{
    new VictimCurHP = GetClientHealth(client);
    new VictimMaxHP = War3_GetMaxHP(client);
    // How much % HP needs to be missing for the buff to trigger
    new BuffTrigger = 7;
    new BuffAmount = RoundToFloor((100 - (float(VictimCurHP) / float(VictimMaxHP) * 100)) / BuffTrigger);
    if (BuffAmount < 0) // Overhealed in TF2
    {
        BuffAmount = 0;
    }
    //PrintHintText(client,"Giving you %d buffs", BuffAmount); Debugger text
    return BuffAmount;
}

public Action:BerserkerTimer(Handle:timer,any:userid) // Check each 0.5 second if the conditions for Berserkers Blood have changed
{
    if(thisRaceID>0)
    {
        for(new i=1;i<=MaxClients;i++)
        {
            if(ValidPlayer(i,true))
            {
                if(War3_GetRace(i)==thisRaceID)
                {
                    new skill = War3_GetSkillLevel(i,thisRaceID,SKILL_BLOOD);
                    new BuffAmount = CalcBuffAmount(i); // How many buffs to apply?
                    new Float:ASPDBuff = (BerserkerBuffASPD[skill] * BuffAmount) + 1.0; // Calculate buff
                    War3_SetBuff(i,fAttackSpeed,thisRaceID,ASPDBuff); // Set the buff
                }
            }
        }
    }
}

public OnW3TakeDmgBullet(victim,attacker,Float:damage){
    if(ValidPlayer(victim,true)&&ValidPlayer(attacker,false)&&GetClientTeam(victim)!=GetClientTeam(attacker))
    {
        if(War3_GetRace(attacker)==thisRaceID)
        {
            // Apply Blood buff
            new skill = War3_GetSkillLevel(attacker,thisRaceID,SKILL_BLOOD);
            new BuffAmount = CalcBuffAmount(attacker);
            new Float:DamageBuff = (BerserkerBuffDamage[skill] * BuffAmount) + 1.0;
            War3_DamageModPercent(DamageBuff);
            
            skill = War3_GetSkillLevel(attacker,thisRaceID,SKILL_SPEAR);
            if(!bSpeared[victim]) // Only reset time when the victim is not speared yet
            {
                bSpeared[victim] = true;
                SpearedBy[victim] = attacker;
                SpearUntil[victim] = GetGameTime() + 3.0;
                if(ClientSpearStacks[victim] < SpearStacks[skill])
                {
                    ClientSpearStacks[victim]++;
                }
            }
            else // Increase the amount of stacks, but don't retrigger the time!
            {
                if(ClientSpearStacks[victim] < SpearStacks[skill])
                {
                    ClientSpearStacks[victim]++;
                }
            }
        }
    }
}

public OnRaceChange(client, oldrace, newrace)
{
    if(newrace!=thisRaceID)
    {
        War3_SetBuff(client,fAttackSpeed,thisRaceID,1.0); // Remove ASPD buff when changing races
    }
}

public OnUltimateCommand(client,race,bool:pressed)
{

	if(race==thisRaceID && pressed && ValidPlayer(client,true) )
	{
		new ult_level=War3_GetSkillLevel(client,race,ULT_BREAK);
		if(ult_level>0)
		{
            new Float:AttackerMaxHP = float(War3_GetMaxHP(client));
            new AttackerCurHP = GetClientHealth(client);
            new SelfDamage = RoundToCeil(AttackerMaxHP * LifeBreakHPConsumed[ult_level]);
            new bool:bUltPossible = SelfDamage < AttackerCurHP;
            if(!Silenced(client)&&War3_SkillNotInCooldown(client,thisRaceID,ULT_BREAK,true))
            {
                if(!bUltPossible)
                {
                    PrintHintText(client, "You do not have enough HP to cast that...");
                }
                else
                {
                    new Float:posVec[3];
                    GetClientAbsOrigin(client,posVec);
                    new Float:otherVec[3];
                    new Float:bestTargetDistance=999999.0; 
                    new team = GetClientTeam(client);
                    new bestTarget = 0;
                    
                    new Float:ultmaxdistance = 300.0;
                    for(new i=1;i<=MaxClients;i++)
                    {
                        if(ValidPlayer(i,true)&&GetClientTeam(i)!=team&&!W3HasImmunity(i,Immunity_Ultimates))
                        {
                            GetClientAbsOrigin(i,otherVec);
                            new Float:dist = GetVectorDistance(posVec,otherVec);
                            if(dist<bestTargetDistance&&dist<ultmaxdistance)
                            {
                                bestTarget = i;
                                bestTargetDistance = GetVectorDistance(posVec,otherVec);
                                
                            }
                        }
                    }
                    if(bestTarget==0)
                    {
                        PrintHintText(client,"No Target Found within %f feet",ultmaxdistance/10);
                    }
                    else
                    {
                    
                        new Float:VictimMaxHP = float(War3_GetMaxHP(bestTarget));
                        new Damage = RoundToFloor(LifeBreakHPTaken[ult_level] * VictimMaxHP);
                        
                        if(War3_DealDamage(bestTarget,Damage,client,DMG_BULLET,"lifebreak")) // do damage to nearest enemy
                        {
                            W3PrintSkillDmgHintConsole(bestTarget,client,War3_GetWar3DamageDealt(),ULT_BREAK); // print damage done
                            W3FlashScreen(bestTarget,RGBA_COLOR_RED); // notify victim he got hurt
                            
                            //EmitSoundToAll(ultimateSound,client);
                            War3_DealDamage(client,SelfDamage,client,DMG_BULLET,"lifebreak"); // Do damage to attacker
                            War3_CooldownMGR(client,10.0,thisRaceID,ULT_BREAK ); // invoke cooldown
                        }
                    }
                }
            }
		}
		else
		{
			PrintHintText(client,"Level Your Ultimate First");
		}
	}
}

public OnAbilityCommand(client,ability,bool:pressed)
{
    if(War3_GetRace(client)==thisRaceID && ability==0 && pressed && IsPlayerAlive(client))
    {
        if(!bSpearActivated[client])
        {
            PrintHintText(client, "Activated Burning Spear");
            bSpearActivated[client] = true;
        }
        else
        {
            PrintHintText(client, "Deactivated Burning Spear");
            bSpearActivated[client] = false;
        }
    }
}